// 6、我们能否让基础数据类型也变成为响应式的呢？
// 在Vue3中，存在ref对象，它是一个简单的包装对象，只具备value一个属性，value的数值即为数据值
// 这个ref对象的设计将与Vue3.0的Composition API之间进行组合。
// ref对象本身可以被响应式追踪，但Vue3中做了特殊处理，作为响应式对象处理时，访问数据需要.value
// 但ref作为模板中数据访问或作为 reactive 对象的属性被访问或修改时，将自动解套出 value 值
// 对于ref对象的设计必要性，可以参考Vue3.0文档原文：
// However, the problem with going reactive-only is that the consumer of a composition function
// must keep the reference to the returned object at all times in order to retain reactivity.
// The object cannot be destructured or spread:

import { log } from "./shared";




// ----->来自模块2的代码
const targetMap = new WeakMap(); // 全局映射：响应式对象--->属性依赖集合
function trigger(targetMap: TargetMap, target: any, key: string | number) {
  const depMap: DepMap | undefined = targetMap.get(target);
  if (!depMap) return;
  const dep: Dep | undefined = depMap.get(key);
  if (dep) {
    dep.forEach((effect) => {
      effect();
    });
  }
}




// ----->来自模块4的代码
const handler = {
  get(target: any, key: string | number, receiver: any) {
    let res: any = Reflect.get(target, key, receiver);
    track(targetMap, target, key); // 注意：在这里收集追踪依赖！！
    return res;
  },
  set(target: any, key: string | number, value: any, receiver: any) {
    let previousValue: any = target[key]; // 旧值
    let res: boolean = Reflect.set(target, key, value, receiver); // 注意先后顺序，先反射再检测值是否需要触发更新
    if (previousValue !== value) {
      trigger(targetMap, target, key);
    }
    return res;
  },
};
function reactive(target: any) {
  return new Proxy(target, handler);
}




// ----->来自模块5的代码
let activeEffect: RunTimeFunction = null; // 用来记录runtime的函数，Vue 2中也运用过这个标记技巧
function effect(eff: Function) {
  activeEffect = eff;
  activeEffect();
  activeEffect = null;
}
function track(targetMap: TargetMap, target: any, key: string | number) {
  if (activeEffect) {
    let depMap: DepMap | undefined = targetMap.get(target);
    if (!depMap) {
      depMap = new Map();
      targetMap.set(target, depMap);
    }
    let dep: Dep | undefined = depMap.get(key);
    if (!dep) {
      dep = new Set();
      depMap.set(key, dep);
    }
    dep.add(activeEffect); // 这里有新改动！将正在运行的函数作为属性依赖加入dep队列中
  }
}




// ----->下面开始的是模块6的代码

// 定义ref函数，使得基础数据类型变成响应式对象
function ref<T>(primitiveValue: RefValue<T>) {
  // Vue 3中使用了对象的存储器计算属性来做这点，当然实际源码逻辑比这个复杂。
  const refObj: Ref<T> = {
    get value() {
      track(targetMap, refObj, "value"); // 收集该对象的依赖
      return primitiveValue;
    },
    set value(newValue) {
      if (primitiveValue !== newValue) {
        primitiveValue = newValue;
        trigger(targetMap, refObj, "value"); // 触发该属性的依赖
      }
    },
  };
  return refObj;
}

// 测试用例6
let product: ProductInfo = reactive({
  price: 5,
  quantity: 10,
});
let total: number = 0;
let salesPrice: Ref<number> = ref(0); // 注意这个0(salesPrice)也开始变成响应式
// 开始收集依赖
effect(() => (total = salesPrice.value * product.quantity));
effect(() => (salesPrice.value = product.price * 0.7));
// 观察响应式
log(total, salesPrice.value, 1);
product.quantity = 3;
log(total, salesPrice.value, 2);
product.price = 10;
log(total, salesPrice.value, 3); // 可以发现salesPrice和total同时变了
